package com.yqh;


import com.yqh.discovery.Registry;
import com.yqh.discovery.RegistryConfig;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 阳秋豪
 * @createTime 2024/2/28
 */
@Slf4j
public class MyrpcBootstrap {
    // MyrpcBootstrap 是一个单例，我们希望每个应用程序只有一个实例
    private static final MyrpcBootstrap myrpcBootstrap = new MyrpcBootstrap();
    // 定义相关的一些基础配置
    private String appName = "default"; // 当前应用名
    private Registry registry; // 注册中心
    private ProtocolConfig protocolConfig; // 协议
    private int port = 8088;
    // 维护已经发布且暴露的服务列表 key -> interface 的全限定名 value -> ServiceConfig
    private static final Map<String, ServiceConfig<?>> SERVICE_LIST = new ConcurrentHashMap<>();

    // 私有的构造方法，构造启动器引导程序时，做一些初始化的事
    private MyrpcBootstrap() {

    }
    /* -------------------------MyrpcBootstrap 实例的相关配置-------------------------*/

    /**
     * 获取启动类的实例
     *
     * @return myrpcBootstrap
     */
    public static MyrpcBootstrap getInstance() {
        return myrpcBootstrap;
    }

    /**
     * 用来定义当前应用的名称
     *
     * @param appName 当前应用名
     * @return this 当前实例
     */
    public MyrpcBootstrap application(String appName) {
        this.appName = appName;
        return this;
    }

    /**
     * 用来配置一个注册中心
     *
     * @param registryConfig 注册中心
     * @return this 当前实例
     */
    public MyrpcBootstrap registry(RegistryConfig registryConfig) {
        // 这里维护一个zookeeper实例，但是，如果这样写就会将zookeeper和当前工程耦合
        // 我们其实是更希望以后可以扩展更多种不同的实现
        // 尝试使用 registryConfig 获取一个注册中心，类似工厂设计模式
        this.registry = registryConfig.getRegistry();
        return this;
    }

    /**
     * 用来配置当前暴露的服务使用的协议
     *
     * @param protocolConfig 协议的封装
     * @return this 当前实例
     */
    public MyrpcBootstrap protocol(ProtocolConfig protocolConfig) {
        this.protocolConfig = protocolConfig;
        if (log.isDebugEnabled()) {
            log.debug("当前工程使用了：{}协议进行序列化", protocolConfig.toString());
        }
        return this;
    }

    /* -------------------------服务提供方的相关api-------------------------*/

    /**
     * 发布服务 -- 实质：将接口以及匹配的实现注册到服务中心
     *
     * @param service 封装的需要发布的服务
     * @return this 当前实例
     */
    public MyrpcBootstrap publish(ServiceConfig<?> service) {
        // 我们抽象了注册中心的概念，使用注册中心的一个实现完成注册
        registry.register(service);
        // 1、当服务调用方，通过接口、方法名、具体的方法参数列表发起调用时，提供方怎么知道使用哪一个实现
        // (1) new 一个 (2) spring beanFactory.getBean(Class) (3) 自己维护映射关系
        SERVICE_LIST.put(service.getInterfaceProvider().getName(), service);
        return this;
    }

    /**
     * 批量发布服务
     *
     * @param services 封装的需要发布的服务集合
     * @return this 当前实例
     */
    public MyrpcBootstrap publish(List<ServiceConfig<?>> services) {
        for (ServiceConfig<?> service : services) {
            this.publish(service);
        }
        return this;
    }

    /**
     * 启动 netty 服务
     */
    public void start() {
        // 1、创建 eventLoop，老板只负责处理请求，之后会将请求分发给 worker
        EventLoopGroup boss = new NioEventLoopGroup(2);
        EventLoopGroup worker = new NioEventLoopGroup(10);
        // 2、需要一个服务器引导程序
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        // 3、配置服务器
        serverBootstrap = serverBootstrap
                .group(boss, worker)
                .channel(NioServerSocketChannel.class) // 创建通道
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        // 核心，我们需要添加很多入站和出站的 handler
                        ch.pipeline().addLast(null);
                    }
                });
        // 4、绑定端口
        ChannelFuture channelFuture = null;
        try {
            channelFuture = serverBootstrap.bind(port).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                boss.shutdownGracefully().sync();
                worker.shutdownGracefully().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    /* -------------------------服务调用方的相关api-------------------------*/
    public MyrpcBootstrap consumer(ConsumerConfig<?> consumerConfig) {
        // 在这个方法里我们是否可以拿到相关的配置项 - 注册中心
        // 配置 reference ，将来调用 get 方法时，方便生产代理对象
        // 1、consumerConfig 需要一个注册中心
        consumerConfig.setRegistry(registry);
        return this;
    }
}

